EC2インスタンスでActive Directoryを作成してみた(インフラ編)
AWS上でActive Directory(AD)サーバーを構築する場合、AWS Managed Microsoft AD、Simple AD、またはAD Connector(オンプレミス環境と連携)といった選択肢があります。しかし、今回はADの学習も兼ねて、EC2インスタンス上にADサーバーを自前で構築します。さらに、別のEC2インスタンスをこのドメインに参加させ、ADユーザーでログインする実践も行います。
この一連の手順を、全3編でお届けする予定です:
- AWSインフラの構築(本記事)
- EC2インスタンス(1台目)へのADサーバーの設定
- EC2インスタンス(2台目)のドメイン参加とADユーザーによるログイン検証
また、今回はコンソール操作による構築ではなく、Terraformによるインフラコード化をして構築しています。
検証するため何度もサーバーを構築し直したり、将来に同様の構築をしたいときに便利です。
構成図
前提
- Terraformで構築
- EC2インスタンスは、Microsoft Windows Server 2022 Base/t2.large
- EC2にRDP接続するためのキーペアは事前に生成しておく(Windowsにログオンするためのパスワード取得で必要)
- 今回は、windows-ad-serverという名称のキーペアにしている
やってみた
ディレクトリ構成
.
├── envs
│ └── dev
│ ├── backend.tf
│ ├── main.tf
│ ├── providers.tf
│ └── variables.tf
└── modules
├── dhcp
│ ├── main.tf
│ └── variables.tf
├── ec2
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── iam
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── security_group
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
├── vpc
│ ├── main.tf
│ ├── outputs.tf
│ └── variables.tf
└── vpc_endpoint
├── main.tf
└── variables.tf
インフラ
■main
locals {
product_name = "shinyat-ec2-windows-ad"
environment = "dev"
}
module "terraform_state_backend" {
source = "cloudposse/tfstate-backend/aws"
namespace = local.product_name
stage = local.environment
name = "terraform"
attributes = ["states"]
terraform_backend_config_file_path = "."
terraform_backend_config_file_name = "backend.tf"
force_destroy = false
}
module "vpc" {
source = "../../modules/vpc"
project_name = local.product_name
environment = local.environment
vpc_cidr = var.vpc_cidr
subnet_a_cidr = var.subnet_a_cidr
subnet_c_cidr = var.subnet_c_cidr
az_a = "ap-northeast-1a"
az_c = "ap-northeast-1c"
}
module "security_group" {
source = "../../modules/security_group"
project_name = local.product_name
environment = local.environment
vpc_id = module.vpc.vpc_id
vpc_cidr = var.vpc_cidr
subnet_a_cidr = var.subnet_a_cidr
}
module "vpc_endpoint" {
source = "../../modules/vpc_endpoint"
project_name = local.product_name
environment = local.environment
vpc_id = module.vpc.vpc_id
subnet_ids = [module.vpc.subnet_a_id]
security_group_ids = [module.security_group.windows_server_security_group_id]
}
module "iam" {
source = "../../modules/iam"
project_name = local.product_name
environment = local.environment
}
module "ec2" {
source = "../../modules/ec2"
project_name = local.product_name
environment = local.environment
subnet_id = module.vpc.subnet_a_id
security_group_ids = [module.security_group.windows_server_security_group_id]
instance_role = module.iam.windows_server_ssm_role_name
}
module "dhcp" {
source = "../../modules/dhcp"
project_name = local.product_name
environment = local.environment
ad_server_private_id = module.ec2.ad_server_private_ip
vpc_id = module.vpc.vpc_id
}
variable "vpc_cidr" {
default = "10.16.0.0/16"
}
variable "subnet_a_cidr" {
default = "10.16.1.0/24"
}
variable "subnet_c_cidr" {
default = "10.16.2.0/24"
}
■dhcp
ADドメインコントローラーに設定したいドメイン名をDHCPオプションセットに登録します。
今回私は、shinyats.comにしましたが、ご自由に変えてください(test.localとか)
ADの設定をするときに、同じドメイン名を設定することになります。
resource "aws_vpc_dhcp_options" "dhcp_options" {
domain_name = "shinyats.com"
domain_name_servers = ["AmazonProvidedDNS", var.ad_server_private_id]
tags = {
Name = "${var.project_name}-${var.environment}-dhcp"
}
}
resource "aws_vpc_dhcp_options_association" "dhcp_options_assoc" {
vpc_id = var.vpc_id
dhcp_options_id = aws_vpc_dhcp_options.dhcp_options.id
}
variable "project_name" {
type = string
}
variable "environment" {
type = string
}
variable "vpc_id" {
type = string
}
variable "ad_server_private_id" {
type = string
}
■ec2
WindowsインスタンスにAdministratorでログオンするために、キーペアが必要なのでEC2インスタンス作成時に紐づけをしています。
今回私は、windows-ad-serverという名称のキーペアを事前に作成、ダウンロードをしており、それを設定しています。
resource "aws_iam_instance_profile" "ssm_profile" {
name = "${var.project_name}-${var.environment}-ssm-profile"
role = var.instance_role
}
resource "aws_instance" "ec2" {
ami = "ami-0f36f4f3d34a4df19" # Microsoft Windows 2022 Datacenter edition. [English]
instance_type = "t2.large"
subnet_id = var.subnet_id
security_groups = var.security_group_ids
iam_instance_profile = aws_iam_instance_profile.ssm_profile.name
key_name = "windows-ad-server"
associate_public_ip_address = false
metadata_options {
http_tokens = "required"
}
tags = {
Name = "${var.project_name}-${var.environment}-ad-server"
}
}
resource "aws_instance" "ec2_user" {
ami = "ami-0f36f4f3d34a4df19" # Microsoft Windows 2022 Datacenter edition. [English]
instance_type = "t2.large"
subnet_id = var.subnet_id
security_groups = var.security_group_ids
iam_instance_profile = aws_iam_instance_profile.ssm_profile.name
key_name = "windows-ad-server"
associate_public_ip_address = false
metadata_options {
http_tokens = "required"
}
tags = {
Name = "${var.project_name}-${var.environment}-ad-user"
}
}
variable "project_name" {
type = string
}
variable "environment" {
type = string
}
variable "subnet_id" {
type = string
}
variable "security_group_ids" {
type = list(string)
}
variable "instance_role" {
type = string
}
output "ad_server_private_ip" {
value = aws_instance.ec2.private_ip
}
■iam
resource "aws_iam_role" "windows_server_ssm_role" {
name = "${var.project_name}-${var.environment}-windows-server-ssm-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}
resource "aws_iam_role_policy_attachment" "ssm_policy_attachment" {
role = aws_iam_role.windows_server_ssm_role.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
variable "project_name" {
type = string
}
variable "environment" {
type = string
}
output "windows_server_ssm_role_name" {
value = aws_iam_role.windows_server_ssm_role.name
}
■security_group
resource "aws_security_group" "windows_server_security_group" {
vpc_id = var.vpc_id
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 3389
to_port = 3389
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [var.vpc_cidr]
}
# 同じプライベートサブネットからのすべての通信を許可
ingress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = [var.subnet_a_cidr]
}
tags = {
Name = "${var.project_name}-${var.environment}-windows-server-sg"
}
}
variable "project_name" {
type = string
}
variable "environment" {
type = string
}
variable "vpc_id" {
type = string
}
variable "vpc_cidr" {
type = string
}
variable "subnet_a_cidr" {
type = string
}
output "windows_server_security_group_id" {
value = aws_security_group.windows_server_security_group.id
}
■vpc
resource "aws_vpc" "vpc" {
cidr_block = var.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "${var.project_name}-${var.environment}-vpc"
}
}
resource "aws_subnet" "private_subnet_a" {
vpc_id = aws_vpc.vpc.id
cidr_block = var.subnet_a_cidr
availability_zone = var.az_a
tags = {
Name = "${var.project_name}-${var.environment}-private-subnet-a"
}
}
resource "aws_subnet" "public_subnet_c" {
vpc_id = aws_vpc.vpc.id
cidr_block = var.subnet_c_cidr
availability_zone = var.az_c
tags = {
Name = "${var.project_name}-${var.environment}-public-subnet-c"
}
}
resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${var.project_name}-${var.environment}-igw"
}
}
resource "aws_eip" "nat_eip" {
domain = "vpc"
}
resource "aws_nat_gateway" "nat" {
allocation_id = aws_eip.nat_eip.id
subnet_id = aws_subnet.public_subnet_c.id
tags = {
Name = "${var.project_name}-${var.environment}-nat"
}
}
resource "aws_route_table" "private_route" {
vpc_id = aws_vpc.vpc.id
tags = {
Name = "${var.project_name}-${var.environment}-private-route"
}
}
resource "aws_route" "private_route" {
route_table_id = aws_route_table.private_route.id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.nat.id
}
resource "aws_route_table_association" "private_rt_assoc" {
subnet_id = aws_subnet.private_subnet_a.id
route_table_id = aws_route_table.private_route.id
}
resource "aws_route_table" "public_route" {
vpc_id = aws_vpc.vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
tags = {
Name = "${var.project_name}-${var.environment}-public-route"
}
}
resource "aws_route_table_association" "public_rt_assoc" {
subnet_id = aws_subnet.public_subnet_c.id
route_table_id = aws_route_table.public_route.id
}
variable "project_name" {
type = string
}
variable "environment" {
type = string
}
variable "vpc_cidr" {
type = string
}
variable "subnet_a_cidr" {
type = string
}
variable "subnet_c_cidr" {
type = string
}
variable "az_a" {
type = string
}
variable "az_c" {
type = string
}
output "vpc_id" {
value = aws_vpc.vpc.id
}
output "subnet_a_id" {
value = aws_subnet.private_subnet_a.id
}
output "route_table_id" {
value = aws_route_table.private_route.id
}
■vpc_endpoint
resource "aws_vpc_endpoint" "ssm_endpoint" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.ap-northeast-1.ssm"
vpc_endpoint_type = "Interface"
subnet_ids = var.subnet_ids
private_dns_enabled = true
security_group_ids = var.security_group_ids
tags = {
Name = "${var.project_name}-${var.environment}-ssm-endpoint"
}
}
resource "aws_vpc_endpoint" "ssm_messages_endpoint" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.ap-northeast-1.ssmmessages"
vpc_endpoint_type = "Interface"
subnet_ids = var.subnet_ids
private_dns_enabled = true
security_group_ids = var.security_group_ids
tags = {
Name = "${var.project_name}-${var.environment}-ssm-messages-endpoint"
}
}
resource "aws_vpc_endpoint" "ec2_messages" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.ap-northeast-1.ec2messages"
vpc_endpoint_type = "Interface"
subnet_ids = var.subnet_ids
private_dns_enabled = true
security_group_ids = var.security_group_ids
tags = {
Name = "${var.project_name}-${var.environment}-ec2-messages-endpoint"
}
}
variable "project_name" {
type = string
}
variable "environment" {
type = string
}
variable "vpc_id" {
type = string
}
variable "subnet_ids" {
type = list(string)
}
variable "security_group_ids" {
type = list(string)
}
Terraform実行コマンド例
Terraformの実行環境の説明は割愛します。
aws-vault exec shinyat -- terraform init
aws-vault exec shinyat -- terraform plan
aws-vault exec shinyat -- terraform apply
動作確認
AWSマネジメントコンソールのEC2から作成したインスタンスを選択し、「接続」を押下します。
RDPクライアント > Fleet Manager を使用して接続する > Fleet Manager Remote Desktop を押下します。
キーペア > ファイル選択 を押下します。
※キーペアはTerraform実行前に手動で作成しています。
キーペアを選択したら、接続を押下します。
接続できました。
さいごに
コードをそのまま利用される際は、あくまで検証用としてご利用ください。
どなたかの参考になれば幸いです。
リンク
このブログは「EC2インスタンスでActive Directoryを作成してみた」という名称で、全3篇でお届けしています。